/*
 * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

module libevent.libevent;

extern(C):

alias ubyte u_char;
version(Windows) {
    private import std.c.windows.winsock;
    private import std.c.windows.windows;
} else {
    private import std.c.linux.linux;
}

enum {
    EVLIST_TIMEOUT  = 0x01,
    EVLIST_INSERTED = 0x02,
    EVLIST_SIGNAL   = 0x04,
    EVLIST_ACTIVE   = 0x08,
    EVLIST_INTERNAL = 0x10,
    EVLIST_INIT     = 0x80,

    /* EVLIST_X_ Private space: 0x1000-0xf000 */
    EVLIST_ALL      = (0xf000 | 0x9f)
}

enum {
    EV_TIMEOUT  = 0x01,
    EV_READ     = 0x02,
    EV_WRITE    = 0x04,
    EV_SIGNAL   = 0x08,
    EV_PERSIST  = 0x10  /* Persistant event */
}

version(TAILQ_ENTRY) {
} else {
/* Fix so that ppl dont have to run with <sys/queue.h> */
    version = _EVENT_DEFINED_TQENTRY;
    struct TAILQ_ENTRY(type) {
        type *tqe_next; /* next element */
        type **tqe_prev;    /* address of previous next element */
    }
}

version(RB_ENTRY) {
} else {
    version = _EVENT_DEFINED_RBENTRY;
    struct RB_ENTRY(type) {
        type *rbe_left;     /* left element */
        type *rbe_right;        /* right element */
        type *rbe_parent;   /* parent element */
        int rbe_color;          /* node color */
    }
}

alias void event_base;

struct event {
    TAILQ_ENTRY!(event) ev_next;
    TAILQ_ENTRY!(event) ev_active_next;
    TAILQ_ENTRY!(event) ev_signal_next;
    RB_ENTRY!(event) ev_timeout_node;

    event_base *ev_base;
    int ev_fd;
    short ev_events;
    short ev_ncalls;
    short *ev_pncalls;  /* Allows deletes in callback */

    timeval ev_timeout;

    int ev_pri;     /* smaller numbers are higher priority */

    void function (int, short, void *arg) ev_callback;
    void *ev_arg;

    int ev_res;     /* result passed to event callback */
    int ev_flags;
};

int EVENT_SIGNAL(event* ev) {
    return ev.ev_fd;
}

int EVENT_FD(event* ev) {
    return ev.ev_fd;
}

version(_EVENT_DEFINED_TQENTRY) {
    //#undef TAILQ_ENTRY
    //#undef _EVENT_DEFINED_TQENTRY
} else {
    //TAILQ_HEAD (event_list, event);
}

version(_EVENT_DEFINED_RBENTRY) {
    //#undef RB_ENTRY
    //#undef _EVENT_DEFINED_RBENTRY
}

struct eventop {
    char *name;
    void *(*init)();
    int (*add)(void *, event *);
    int (*del)(void *, event *);
    int (*recalc)(event_base *, void *, int);
    int (*dispatch)(event_base *, void *, timeval *);
};

// #define TIMEOUT_DEFAULT  {5, 0}

void *event_init();
int event_dispatch();
int event_base_dispatch(event_base *);

enum {
    _EVENT_LOG_DEBUG = 0,
    _EVENT_LOG_MSG   = 1,
    _EVENT_LOG_WARN  = 2,
    _EVENT_LOG_ERR   = 3,
}

typedef void (*event_log_cb)(int severity, char *msg);
void event_set_log_callback(event_log_cb cb);

/* Associate a different event base with an event */
int event_base_set(event_base *, event *);

enum {
    EVLOOP_ONCE     = 0x01,
    EVLOOP_NONBLOCK = 0x02
}

int event_loop(int);
int event_base_loop(event_base *, int);
int event_loopexit(timeval *);  /* Causes the loop to exit */
int event_base_loopexit(event_base *, timeval *);

alias event_add evtimer_add;

void evtimer_set(event* ev, void function(int, short, void*) cb, void* arg) {
    event_set(ev, -1, 0, cb, arg);
}

alias event_del evtimer_del;

int evtimer_pending(event* ev, timeval* tv) {
    return event_pending(ev, EV_TIMEOUT, tv);
}

int evtimer_initialized(event* ev) {
    return ev.ev_flags & EVLIST_INIT;
}

alias event_add timeout_add;

void timeout_set(event* ev, void function(int, short, void*) cb, void* arg) {
    event_set(ev, -1, 0, cb, arg);
}

alias event_del timeout_del;

int timeout_pending(event* ev, timeval* tv) {
    return event_pending(ev, EV_TIMEOUT, tv);
}

int timeout_initialized(event* ev) {
    return ev.ev_flags & EVLIST_INIT;
}

alias event_add signal_add;

void signal_set(event* ev, int x, void function(int, short, void*) cb, void* arg) {
    event_set(ev, x, EV_SIGNAL | EV_PERSIST, cb, arg);
}

alias event_del signal_del;

int signal_pending(event* ev, timeval* tv) {
    return event_pending(ev, EV_SIGNAL, tv);
}

int signal_initialized(event* ev) {
    return ev.ev_flags & EVLIST_INIT;
}

void event_set(event *, int, short, void (*)(int, short, void *), void *);
int event_once(int, short, void (*)(int, short, void *), void *, timeval *);

int event_add(event *, timeval *);
int event_del(event *);
void event_active(event *, int, short);

int event_pending(event *, short, timeval *);

version(Windows) {
    bool event_initialized(event* ev) {
        return (ev.ev_flags & EVLIST_INIT) != 0 && ev.ev_fd != cast(int)INVALID_HANDLE_VALUE;
    }
} else {
    bool event_initialized(event* ev) {
        return (ev.ev_flags & EVLIST_INIT) != 0;
    }
}

/* Some simple debugging functions */
char *event_get_version();
char *event_get_method();

/* These functions deal with event priorities */

int event_priority_init(int);
int event_base_priority_init(event_base *, int);
int event_priority_set(event *, int);

/* These functions deal with buffering input and output */

struct evbuffer {
    u_char *buffer;
    u_char *orig_buffer;

    size_t misalign;
    size_t totallen;
    size_t off;

    void (*cb)(evbuffer *, size_t, size_t, void *);
    void *cbarg;
}

/* Just for error reporting - use other constants otherwise */
enum {
    EVBUFFER_READ       = 0x01,
    EVBUFFER_WRITE      = 0x02,
    EVBUFFER_EOF        = 0x10,
    EVBUFFER_ERROR      = 0x20,
    EVBUFFER_TIMEOUT    = 0x40,
}

typedef void (*evbuffercb)(bufferevent *, void *);
typedef void (*everrorcb)(bufferevent *, short what, void *);

struct event_watermark {
    size_t low;
    size_t high;
}

struct bufferevent {
    event ev_read;
    event ev_write;

    evbuffer *input;
    evbuffer *output;

    event_watermark wm_read;
    event_watermark wm_write;

    evbuffercb readcb;
    evbuffercb writecb;
    everrorcb errorcb;
    void *cbarg;

    int timeout_read;   /* in seconds */
    int timeout_write;  /* in seconds */

    short enabled;  /* events that are currently enabled */
}

bufferevent *bufferevent_new(int fd,
    evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
int bufferevent_priority_set(bufferevent *bufev, int pri);
void bufferevent_free(bufferevent *bufev);
int bufferevent_write(bufferevent *bufev, void *data, size_t size);
int bufferevent_write_buffer(bufferevent *bufev, evbuffer *buf);
size_t bufferevent_read(bufferevent *bufev, void *data, size_t size);
int bufferevent_enable(bufferevent *bufev, short event);
int bufferevent_disable(bufferevent *bufev, short event);
void bufferevent_settimeout(bufferevent *bufev,
    int timeout_read, int timeout_write);


u_char* EVBUFFER_DATA(evbuffer* x) {
    return x.buffer;
}
size_t EVBUFFER_LENGTH(evbuffer* x) {
    return x.off;
}
evbuffer* EVBUFFER_INPUT(bufferevent* x) {
    return x.input;
}
evbuffer* EVBUFFER_OUTPUT(bufferevent* x) {
    return x.output;
}

evbuffer *evbuffer_new();
void evbuffer_free(evbuffer *);
int evbuffer_expand(evbuffer *, size_t);
int evbuffer_add(evbuffer *, void *, size_t);
int evbuffer_remove(evbuffer *, void *, size_t);
char *evbuffer_readline(evbuffer *);
int evbuffer_add_buffer(evbuffer *, evbuffer *);
int evbuffer_add_printf(evbuffer *, char *fmt, ...);
void evbuffer_drain(evbuffer *, size_t);
int evbuffer_write(evbuffer *, int);
int evbuffer_read(evbuffer *, int, int);
u_char *evbuffer_find(evbuffer *, u_char *, size_t);
void evbuffer_setcb(evbuffer *, void (*)(evbuffer *, size_t, size_t, void *), void *);

